/*
 * @Author: zhong_m
 * @Date: 2021-11-24 18:42:32
 * @LastEditTime: 2023-06-30 10:49:53
 * @LastEditors: cytra
 * @Description: 切换主题
 * @FilePath: \central-system\src\utils\theme\index.js
 */
import {theme} from '@/assets/css/variables.scss'
import Vue from 'vue'
class ThemeControl {
  constructor() {
    this.default = {
      theme: theme,
      themeCluster: this.getThemeCluster(theme),
      themeRgb: [
        parseInt(theme.slice(1, 3), 16),
        parseInt(theme.slice(3, 5), 16),
        parseInt(theme.slice(5), 16)
      ]
    }
    this.otheme = theme
    this.ntheme = ''
    this.styleFilterMap = new WeakMap()
    this.styles = this.getStyles()
    this.othemeCluster = this.default.themeCluster
    this.othemeRgb = this.default.themeRgb
    this.nthemeCluster = []
    this.styles.forEach(style => {
      this.getAlphaList(style, theme)
    })
    this.initObserve()
    this.transformLinkTag()
  }
  initObserve() {
    // 监听变化，同步转换link并将style加入styles
    const mutationObs = new MutationObserver((list) => {
      let linkList = list.flatMap(item => {
        return item.addedNodes?.[0]?.nodeName === 'LINK' ? [item.addedNodes[0]] : []
      })
      linkList.forEach(item => {
        this.linkToStyle(item)
      })
      let styleList = list.flatMap(item => {
        return item.addedNodes?.[0]?.nodeName === 'STYLE' ? [item.addedNodes[0]] : []
      })
      styleList.forEach(style => {
        this.getAlphaList(style, theme)
        this.updateStyle(style, this.default.themeCluster, this.default.themeRgb)

      })
      this.styles.push(...styleList)
      // console.log(styleList, linkList, this.styles)

    })
    mutationObs.observe(document.head, { childList: true })
  }
  transformLinkTag() {
    let linkList = [].slice.call(document.querySelectorAll('head link[rel=stylesheet]'))
    linkList.forEach(item => {
      this.linkToStyle(item)
    })
  }
  insertAfter(node, target) {
    const parent = target.parentNode
    if (parent.lastChild == target) {
      parent.appendChild(node)
    } else {
      parent.insertBefore(node, target.nextsilbing)
    }
  }

  linkToStyle(link) {
    this.getCSSString(link.href || link.getAttribute('data-href')).then(res => {
      let styleTag = document.createElement('style')
      styleTag.innerText = res
      styleTag.setAttribute('data-url', link.href)
      // link.parentNode?.replaceChild(styleTag, link)
      this.insertAfter(styleTag, link)
    })

  }
  updateTheme(ntheme) {
    if (this.ntheme) {
      this.otheme = this.ntheme
      this.othemeCluster = this.nthemeCluster

    }

    this.ntheme = ntheme
    this.nthemeCluster = this.getThemeCluster(this.ntheme)
    this.styles.forEach(style => {
      this.updateStyle(style)
    })
  }
  updateStyle(style, othemeCluster = this.othemeCluster) {
    // console.log(this.ntheme, this.styleFilterMap)
    if (!this.ntheme) return
    let { innerText } = style
    if (!innerText) return
    if (this.styleFilterMap.has(style) && this.styleFilterMap.get(style)) {
      this.styleFilterMap.get(style).forEach((color, index) => {
        const ncolor = this.nthemeCluster[othemeCluster.indexOf(color)]
        if (new RegExp(color, 'ig').test(innerText)) {
          innerText = innerText.replace(new RegExp(color, 'ig'), ncolor)
        }
        this.styleFilterMap.get(style)[index] = ncolor
      })
    }
    // 当otheme === 默认值，说明是首次更新主题、将rgba类型转为hex类型，并新增到map值
    if(this.otheme === this.default.theme) {
      let defaultRgb = this.default.themeRgb
      // 更新rgba类颜色
      if (new RegExp(`rgba\\(${defaultRgb.join(',\\s?')}`, 'ig').test(innerText)) {
        innerText = innerText.replace(
          new RegExp(`rgba\\(${defaultRgb.join(',\\s?')}(,(.+?))?\\)`, 'ig'),
          (...match) => {
            return `${this.ntheme}${this.alphaToHex(match[2])}`
          }
        )
        // 新增ntheme到map值
        let colorList = this.styleFilterMap.get(style) ?? []
        colorList.includes(this.ntheme) || colorList.push(this.ntheme)
        this.styleFilterMap.set(style, colorList)

      }
    }
    if(this.styleFilterMap.get(style)) {
      style.innerText = innerText
    } 

  }
  alphaToHex(alpha) {
    let hex = parseInt(alpha * 255)
      .toString(16)
      .toUpperCase()
    return hex.length > 1 ? hex : '0' + hex
  }
  // 绝对路径
  getHostPort(url) {
    url = url.match(/https?:\/\/([^/]+)\//i);
    let domain = '';
    if (url && url[1]) {
      domain = url[0].substring(0, url[0].length - 1);
    }
    return domain;
  }
  // 请求css样式、修改路径
  getCSSString(url) {
    return new Promise(resolve => {
      const xhr = new XMLHttpRequest()
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4 && xhr.status === 200) {
          resolve(xhr.responseText.replace(/url\(["']?([\.\/\w\d_\-\#\?]+)["']?\)/gi,(...item) => {
            // *不能把url()函数内引用绝对路径的地址做替换
            if (item[0].startsWith('url(/')) {
              return `url("${this.getHostPort(url) + item[1]}")`
            } else {
              return `url("${url.substring(0, url.lastIndexOf('/')+1) + item[1]}")`
            }
          }))
        }
      }
      xhr.open('GET', url) 
      xhr.send()
    })
  }
  getStyles() {
    let styles = []
    if (Vue.prototype.$InQianKun) {
      styles = [].slice.call(
        document.querySelector(`#QKframe [data-name=${Vue.prototype.$appName}]`)
          .querySelectorAll('style'))
    } else {
      styles = [].slice.call(document.querySelectorAll('head style'))
    }
    return styles
  }
  // 获取主题色组
  getAlphaList(style, color) {
    let cluster = this.getThemeCluster(color, 100)
    let useColorAlpha = []
    let { innerText } = style
    let colorList = []
    if (typeof innerText !== 'string') {
      this.styleFilterMap.set(style, null)
    }
    cluster.forEach((color, index) => {
      if (new RegExp(color, 'ig').test(innerText)) {
        useColorAlpha.push([cluster[index], index])
        colorList.push(color)
      }
    })
    this.styleFilterMap.set(style, colorList.length ? colorList : null)
  }

  getThemeCluster(theme, fix = 100) {
    const tintColor = (color, tint) => {
      let red = parseInt(color.slice(1, 3), 16)
      let green = parseInt(color.slice(3, 5), 16)
      let blue = parseInt(color.slice(5), 16)
      if (tint === 0) {
        // when primary color is in its rgb space
        return [red, green, blue].join(',')
      } else {
        red += Math.round(tint * (255 - red))
        green += Math.round(tint * (255 - green))
        blue += Math.round(tint * (255 - blue))
        red = (red > 15 ? '' : '0') + red.toString(16)
        green = (green > 15 ? '' : '0') + green.toString(16)
        blue = (blue > 15 ? '' : '0') + blue.toString(16)
        return `#${red}${green}${blue}`
      }
    }
    const shadeColor = (color, shade) => {
      let red = parseInt(color.slice(1, 3), 16)
      let green = parseInt(color.slice(3, 5), 16)
      let blue = parseInt(color.slice(5), 16)
      red = Math.round((1 - shade) * red)
      green = Math.round((1 - shade) * green)
      blue = Math.round((1 - shade) * blue)
      red = (red > 15 ? '' : '0') + red.toString(16)
      green = (green > 15 ? '' : '0') + green.toString(16)
      blue = (blue > 15 ? '' : '0') + blue.toString(16)
      return `#${red}${green}${blue}`
    }
    const clusters = [theme]
    for (let i = 1; i < fix; i++) {
      clusters.push(tintColor(theme, Number((i / fix).toFixed(2))))
    }
    clusters.push(shadeColor(theme, 0.1))
    return clusters
  }
}

let themeControl = null
export function themeInit(ntheme) {
  themeControl = new ThemeControl()
  if (ntheme) {
    themeControl.updateTheme(ntheme)
  }
}
export function updateStyle(ntheme) {
  if (themeControl) {
    themeControl.updateTheme(ntheme)
  } else {
    themeInit(ntheme)
  }

}